#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <sys/types.h>
#include <dirent.h>

#include <list>
#include <string>

#include "NameSvr.h"
#include "ProtoHelper.h"
#include "NameSvrCommon.h"
#include "NameSvrProtoHelper.h"
#include "CnfSvrClient.h"

using std::cout;
using std::endl;
using std::list;
using std::string;

#define MDS_STATUS_OK 0

#define SFS_FILE_TYPE_DIR 1
#define SFS_FILE_TYPE_REG_FILE 2

LoggerPtr NameSvr::_logger = Logger::getLogger("NameSvr");

int NameSvr::init(const char * log_conf_file, const char * cs_ip, unsigned short cs_port, int cs_conn_pool_size, \
				int cs_socket_conn_timeout, int cs_socket_send_timeout, int cs_socket_recv_timeout, \
				int cs_socket_max_response_size) {
	if (NULL == cs_ip ||  NULL == log_conf_file) {
		LOG4CXX_ERROR(_logger, "init error, invalid args");
		return -1;
	}
	 
	_sfw.init_logger(log_conf_file);
	
	int ret = _csc.init(cs_ip, cs_port, cs_conn_pool_size, cs_socket_conn_timeout, cs_socket_send_timeout, \
					cs_socket_recv_timeout, cs_socket_max_response_size);
	if (ret < 0) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", cnf svr init error, ret:" << ret);
		return -2;
	}

	NS_CNF ns;
        ret = _csc.get_ns_cnf(ns);
        if (ret < 0) {
                LOG4CXX_ERROR(_logger, __FUNCTION__ << ", could not get ns cnf, ret:" << ret);
                return -3;
        }

	_listen_ip = ns.ip;
	_listen_port = ns.port;
	_thread_num = ns.thread_num;
	_log_conf_file = log_conf_file;
	_socket_idle_timeout = ns.socket_idle_timeout;
	_socket_send_timeout = ns.socket_send_timeout;
	_socket_recv_timeout = ns.socket_recv_timeout;
	_max_request_size = ns.socket_max_request_size;
	_root_data_path = ns.root_data_path;

	srand(time(NULL));

	ret = _sfw.init(ns.thread_num, handle_socket_input_func, this, ns.ip.c_str(), ns.port, ns.socket_idle_timeout, NULL, NULL);
	if (ret < 0) {
		LOG4CXX_ERROR(_logger, "sfw init error, ret:" << ret);
		return -2;
	}

	LOG4CXX_INFO(_logger, "init NameSvr OK, listen_ip:" << _listen_ip << ", listen_port:" << _listen_port << \
				", thread_num:" << \
				_thread_num << ", log_conf_file:" << _log_conf_file << ", socket_idle_timeout:" << \
				_socket_idle_timeout << ", socket_send_timeout:" << _socket_send_timeout << \
				", socket_recv_timeout:" << _socket_recv_timeout << ", max_request_size:" << _max_request_size << \
				", root_data_path:" << _root_data_path);
	return 1;
}

int NameSvr::start() {
	return _sfw.start_server();
}

int NameSvr::handle_socket_input_func(int fd, void * arg) {
	NameSvr * ns = (NameSvr *)arg;
        json_object * j;
        int cmd_id;
        string request;
        int ret = recv_and_parse_request(fd, ns->_socket_recv_timeout, ns->_max_request_size, j, cmd_id, request);
        if (ret < 0) {
                LOG4CXX_ERROR(_logger, "recv and parse request error, ret:" << ret);
                return -1;
        }
        string response;
        ns->dispatch_request(cmd_id, j, response);
        json_object_put(j);
        if (response.size() < 1) response = build_error_response(NS_ERR_UNKNOWN_ERR);
        ret = send_cmd(fd, ns->_socket_send_timeout, response.data(), response.size());
        if (ret < 0) {
                LOG4CXX_ERROR(_logger, "reply cmd error, ret:" << ret << ", fd:" << fd << ", request:" << request << \
                                        ", response:" << response);
                return -2;
        }
        return 2;
}

int NameSvr::dispatch_request(int cmd_id, json_object * j, string & response) {
	int ret = -1;
        switch (cmd_id) {
                case NS_CMD_CREATE_FILE:
                        ret = handle_create_file(j, response);
                        break;
                case NS_CMD_DELETE_FILE:
                        ret = handle_delete_file(j, response);
                        break;
		case NS_CMD_CREATE_DIR:
                        ret = handle_create_dir(j, response);
                        break;
		case NS_CMD_DELETE_DIR:
                        ret = handle_delete_dir(j, response);
                        break;
		case NS_CMD_GET_FILE_METADATA_INFO:
			ret = handle_get_file_metadata_info(j, response);
			break;
		case NS_CMD_ALLOC_MDS_4_NEW_FILE:
			ret = handle_alloc_mds_4_new_file(j, response);
			break;
		case NS_CMD_QUERY_IS_FILE_EXIST:
			ret = handle_query_is_file_exist(j, response);
			break;
		case NS_CMD_QUERY_IS_DIR_EXIST:
			ret = handle_query_is_dir_exist(j, response);
			break;
		case NS_CMD_READ_DIR:
			ret = handle_read_dir(j, response);
			break;
        
                default:
                        LOG4CXX_ERROR(_logger, "unknown command, cmd_id:" << cmd_id);
                        ret = -100;
        }
        return ret;
}

int NameSvr::handle_create_file(json_object * j, string & response) {
	json_object * j_mds_id = json_object_object_get(j, "MDS_ID");	
	json_object * j_dir_id = json_object_object_get(j, "DIR_ID");	
	json_object * j_file_id = json_object_object_get(j, "FILE_ID");
	json_object * j_file_path = json_object_object_get(j, "FILE_PATH");
	if (is_error(j_mds_id) || is_error(j_dir_id) || is_error(j_file_id) || is_error(j_file_path)) {
                LOG4CXX_ERROR(_logger, "in handle create file, args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -1;
        }
	int mds_id = json_object_get_int(j_mds_id);
	int dir_id = json_object_get_int(j_dir_id);
	int file_id = json_object_get_int(j_file_id);
	string file_path = json_object_get_string(j_file_path);
	if (mds_id < 0 || dir_id < 0 || file_id < 1) {
	        LOG4CXX_ERROR(_logger, "in handle create file, args error:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -2;
        }
	string file_content = build_file_metadata_svr_record(mds_id, dir_id, file_id);
	string file_local_path = _root_data_path + file_path;
	struct stat s;
	int ret = stat(file_local_path.c_str(), &s);
	if (0 == ret)  {
		LOG4CXX_ERROR(_logger, "in handle create file,  file already exist, file:" << file_local_path);
		response = build_error_response(NS_ERR_FILE_ALREADY_EXIST, "");
		return -3;
	}
	FILE * fp = fopen(file_local_path.c_str(), "w");
	if (NULL == fp) {
		LOG4CXX_ERROR(_logger, "in handle create file, file create error, file:" << file_local_path << ", errno:" << \
				errno);
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR, "");
		return -4;
	}
	int n = fwrite(file_content.c_str(), file_content.size(), 1, fp);
	fclose(fp);
	if (1 != n) {
		LOG4CXX_ERROR(_logger, "in handle create file, file write error, file:" << file_local_path << ", errno:" << \
				errno);
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR, "");
		return -5;
	}
	LOG4CXX_INFO(_logger, "file create ok, file:" << file_local_path << ", content:" << file_content);
	response = build_ok_response();
	return 1;
}

int NameSvr::handle_delete_file(json_object * j, string & response) {
	json_object * j_file_path = json_object_object_get(j, "FILE_PATH");
	if (is_error(j_file_path)) {
                LOG4CXX_ERROR(_logger, "in handle delete file, args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -1;
        }
	string file_path = json_object_get_string(j_file_path);
	string file_local_path = _root_data_path + file_path;
	struct stat s;
	int ret = stat(file_local_path.c_str(), &s);
	if (0 != ret)  {
		if (ENOENT == errno) {
			LOG4CXX_ERROR(_logger, "in handle delete file,  file not exist, file:" << file_local_path);
			response = build_error_response(NS_ERR_FILE_NOT_EXIST, "");
			return -2;
		}
		else {
			LOG4CXX_ERROR(_logger, "in handle delete file , stat file error:" << file_local_path << ", stat ret:" << \
					ret << ", errno:" << errno);
			response = build_error_response(NS_ERR_OS_FILE_OP_ERR, "");
			return -3;
		}
	}
	if (!S_ISREG(s.st_mode)) {
			LOG4CXX_ERROR(_logger, "try to delete a file but the input file path is not a file:" << file_local_path);
			response = build_error_response(NS_ERR_TARGET_NOT_A_FILE, "");
			return -4;
	}
	ret = unlink(file_local_path.c_str());
	if (0 != ret) {
		LOG4CXX_ERROR(_logger, "in handle delete file, unlink file error, file:" << file_local_path << ", ret:" << ret << \
				", errno:" << errno);
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR, "");
		return -4;
	}
	LOG4CXX_INFO(_logger, "delete file ok, file:" << file_local_path);
	response = build_ok_response();
	return 1;

}

int NameSvr::handle_create_dir(json_object * j, string & response) {
	json_object * j_dir_path = json_object_object_get(j, "DIR_PATH");
	if (is_error(j_dir_path)) {
                LOG4CXX_ERROR(_logger, "in handle create dir, args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -1;
        }
	string dir_path = json_object_get_string(j_dir_path);
	string dir_local_path = _root_data_path + dir_path;
	struct stat s;
	int ret = stat(dir_local_path.c_str(), &s);
	if (0 == ret)  {
		LOG4CXX_ERROR(_logger, "in handle create dir,  dir already exist, dir:" << dir_local_path);
		response = build_error_response(NS_ERR_DIR_ALREADY_EXIST, "");
		return -2;
	}
	ret = mkdir(dir_local_path.c_str(), 0755);
	if (0 != ret) {
		LOG4CXX_ERROR(_logger, "in handle create dir, mkdir fail, dir:" << dir_local_path << ", ret:" << ret << \
				", errno:" << errno);
		response = build_error_response(NS_ERR_OS_DIR_OP_ERR);
		return -3;
	}
	LOG4CXX_INFO(_logger, "create dir ok, dir:" << dir_local_path);
	response = build_ok_response();
	return 1;
}

int NameSvr::handle_delete_dir(json_object * j, string & response) {
	json_object * j_dir_path = json_object_object_get(j, "DIR_PATH");
	if (is_error(j_dir_path)) {
                LOG4CXX_ERROR(_logger, "in handle delete dir, args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -1;
        }
	string dir_path = json_object_get_string(j_dir_path);
	string local_dir_path = _root_data_path + dir_path;
	int ret = rmdir(local_dir_path.c_str());
	if (0 != ret) {
		LOG4CXX_ERROR(_logger, "in handle delete dir, rmdir fail, dir:" << local_dir_path << ", ret:" << ret << \
				", errno:" << errno);
		response = build_error_response(NS_ERR_OS_DIR_OP_ERR);
		return -2;
	}
	LOG4CXX_INFO(_logger, "delete dir ok, dir:" << local_dir_path);
	response = build_ok_response();
	return 1;
}

int NameSvr::handle_alloc_mds_4_new_file(json_object * j, string & response) {
	list<MDS_CNF> l_mds;
	int ret = _csc.get_all_mds_cnf(l_mds);
	if (ret < 0) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", get all mds cnf error, ret:" << ret);
		response = build_error_response(NS_ERR_OUTER_SERVICE_ERR);
		return -1;
	}
	int r = rand();
	int total_weight = 0;
	for (list<MDS_CNF>::const_iterator iter = l_mds.begin(); l_mds.end() != iter; iter++) {
		if (iter->weight <= 0 || MDS_STATUS_OK != iter->status) continue;
		total_weight += iter->weight;
	}
	if (total_weight < 1) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", total weight is 0" << ret);
		response = build_error_response(NS_ERR_OUTER_SERVICE_ERR);
		return -2;
	}
	r = r % total_weight;
	int t = 0;
	int mds_id = -1;
	for (list<MDS_CNF>::const_iterator iter = l_mds.begin(); l_mds.end() != iter; iter++) {
		if (iter->weight <= 0 || MDS_STATUS_OK != iter->status) continue;
		t += iter->weight;
		if (t >= r) { mds_id = iter->id; break; }
	}
	response = ns_build_alloc_mds_4_new_file_response(mds_id);
	LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
	return 1;
}


int NameSvr::handle_get_file_metadata_info(json_object * j, string & response) {
	json_object * j_file_path = json_object_object_get(j, "FILE_PATH");
	if (is_error(j_file_path)) {
                LOG4CXX_ERROR(_logger, __FUNCTION__ << " error, args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS, "");
		return -1;
        }
	string file_path = json_object_get_string(j_file_path);
	string file_local_path = _root_data_path + file_path;
	struct stat ss;
	int ret = stat(file_local_path.c_str(), &ss);
	if (0 != ret) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", stat error, file:" << file_local_path << ", errno:" << errno);
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR);
		return -2;
	}
	int32_t num_hard_link, file_type, mds_id, dir_id;
	int64_t file_id;
	if (S_ISDIR(ss.st_mode)) file_type = SFS_FILE_TYPE_DIR;
	else if (S_ISREG(ss.st_mode)) file_type = SFS_FILE_TYPE_REG_FILE;
	else {
		LOG4CXX_ERROR(_logger, "stat found file:" << file_local_path << " is neither a dir nor a normal file");
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR);
		return -3;
	}
	if (SFS_FILE_TYPE_REG_FILE == file_type) {
		int ret = get_file_metadata_record(file_local_path.c_str(), mds_id, dir_id, file_id);
		if (ret < 0) {
			LOG4CXX_ERROR(_logger, __FUNCTION__ << ", get_file_metadata_record error, file:" << file_local_path << \
					", ret:" << ret);
			response = build_error_response(NS_ERR_OS_FILE_OP_ERR);
			return -4;
		}
	}
	num_hard_link = ss.st_nlink;
	response = ns_build_get_file_metadata_info_response(num_hard_link, file_type, mds_id, dir_id, file_id);
	LOG4CXX_INFO(_logger, "get file metadata svr info ok, file:" << file_local_path << ", response:" << response);
	return 1;
}

int NameSvr::handle_query_is_file_exist(json_object * j, string & response) {
	json_object * j_file_path = json_object_object_get(j, "FILE_PATH");
	if (NULL == j_file_path) {
                LOG4CXX_ERROR(_logger, __FUNCTION__ << ", args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS);
		return -1;
        }
	string file_path = json_object_get_string(j_file_path);
	string file_local_path = _root_data_path + file_path;
	struct stat s;
	int ret = stat(file_local_path.c_str(), &s);
	if (0 != ret) {
		if (ENOENT == errno) {
			response = ns_build_query_is_file_exist_response(-1);
			LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
			return 1;
		}
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR);
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", stat error, file:" << file_local_path << ", errno:" << errno);
		return -2;
	}
	response = ns_build_query_is_file_exist_response(1);
	LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
	return 2;
}

int NameSvr::handle_query_is_dir_exist(json_object * j, string & response) {
	json_object * j_dir_path = json_object_object_get(j, "DIR_PATH");
	if (NULL == j_dir_path) {
                LOG4CXX_ERROR(_logger, __FUNCTION__ << ", args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS);
		return -1;
        }
	string dir_path = json_object_get_string(j_dir_path);
	string dir_local_path = _root_data_path + dir_path;
	struct stat s;
	int ret = stat(dir_local_path.c_str(), &s);
	if (0 != ret) {
		if (ENOENT == errno) {
			response = ns_build_query_is_dir_exist_response(-1);
			LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
			return 1;
		}
		response = build_error_response(NS_ERR_OS_FILE_OP_ERR);
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", stat error, file:" << dir_local_path << ", errno:" << errno);
		return -2;
	}
	response = ns_build_query_is_dir_exist_response(1);
	LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
	return 2;
}

string NameSvr::build_file_metadata_svr_record(int mds_id, int dir_id, int file_id) {
	if (mds_id < 0 || dir_id < 0 || file_id < 0) {
		LOG4CXX_ERROR(_logger, "build file metadata svr record error, invalid args, mds_id:" << mds_id << ", dir_id:" << \
				dir_id << ", file_id:" << file_id);
		return "";
	}
	json_object * j = json_object_new_object();
	json_object * j_mds_id = json_object_new_int(mds_id);
	json_object * j_dir_id = json_object_new_int(dir_id);
	json_object * j_file_id = json_object_new_int(file_id);
	json_object_object_add(j, "MDS_ID", j_mds_id);
	json_object_object_add(j, "DIR_ID", j_dir_id);
	json_object_object_add(j, "FILE_ID", j_file_id);
	string content = json_object_to_json_string(j);
	json_object_put(j);
	return content;
}

int NameSvr::handle_read_dir(json_object * j, string & response) {
	json_object * j_dir_path = json_object_object_get(j, "DIR_PATH");
	if (NULL == j_dir_path) {
                LOG4CXX_ERROR(_logger, __FUNCTION__ << ", args missing:" << json_object_to_json_string(j));
		response = build_error_response(NS_ERR_INVALID_ARGS);
		return -1;
        }
	string dir_path = json_object_get_string(j_dir_path);
	string dir_local_path = _root_data_path + dir_path;
	DIR * d = opendir(dir_local_path.c_str());
	if (NULL == d) {
		build_error_response(NS_ERR_OS_FILE_OP_ERR);
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", open dir error, request:" << json_object_to_json_string(j) << \
				", errno:" << errno);
		return -2;
	}
	list<string> entries;
	while (true) {
		struct dirent * e = readdir(d);
		if (NULL == e) break;
		entries.push_back(e->d_name);
	}
	closedir(d);
	response = ns_build_read_dir_response(entries);
	LOG4CXX_INFO(_logger, __FUNCTION__ << ", ok, response:" << response);
	return 2;
}

int NameSvr::get_file_metadata_record(const char * file_path, int32_t & mds_id, int32_t & dir_id, int64_t & file_id) {
	if (NULL == file_path) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", invalid args");
		return -1;
	}
	struct stat s;
	int ret = stat(file_path, &s);
	if (0 != ret) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", stat error, file:" << file_path << ", errno:" << errno);
		return -2;
	}
	FILE * fp = fopen(file_path, "r");
	if (NULL == fp) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", fopen error, file:" << file_path << ", errno:" << errno);
		return -3;
	}
	char * buf = new char[s.st_size + 1];
	bzero(buf, s.st_size + 1);
	int n = fread(buf, s.st_size, 1, fp);
	fclose(fp);
	if (1 != n) {
		delete[] buf;
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", fread error, file:" << file_path << ", errno:" << errno);
		return -4;
	}
	json_object * j = json_tokener_parse(buf);
	if (is_error(j)) {
		LOG4CXX_ERROR(_logger, __FUNCTION__ << ", json_tokener_parse error, file:" << file_path << ", content:" << \
				buf);
		delete[] buf;
		return -5;
	}
	json_object * j_mds_id = json_object_object_get(j, "MDS_ID");
	json_object * j_dir_id = json_object_object_get(j, "DIR_ID");
	json_object * j_file_id = json_object_object_get(j, "FILE_ID");
	mds_id = json_object_get_int(j_mds_id);
	dir_id = json_object_get_int(j_dir_id);
	file_id = json_object_get_int(j_file_id);
	json_object_put(j);
	delete[] buf;
	return 1;
}
